package services;

import java.util.Collection;
import java.util.Date;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;

import javax.transaction.Transactional;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.authentication.encoding.Md5PasswordEncoder;
import org.springframework.stereotype.Service;
import org.springframework.util.Assert;

import repositories.CustomerRepository;
import repositories.OrderRepository;
import repositories.PurchasedItemRepository;
import security.Authority;
import security.LoginService;
import security.UserAccount;
import domain.Administrator;
import domain.AvailableItem;
import domain.Customer;
import domain.Order;
import domain.PurchasedItem;
import domain.ShoppingCartItemUnit;
import forms.CustomerForm;

@Service
@Transactional
public class CustomerService {

	// Managed Repository

	@Autowired
	private CustomerRepository customerRepository;

	@Autowired
	private OrderRepository orderRepository;
	@Autowired
	private PurchasedItemRepository purchasedItemRepository;

	@Autowired
	private AdministratorService administratorService;

	// Managed Services

	// Constructors

	public CustomerService() {
		super();
	}

	// Simple CRUD methods

	public Customer create() {
		Customer result;
		UserAccount userAccount;
		Authority authority;
		Collection<Order> orders;
		Collection<PurchasedItem> purchasedItems;
		Collection<ShoppingCartItemUnit> shoppingCartItemUnits;
		Collection<AvailableItem> itemsToSell;
		Collection<AvailableItem> wishedItems;

		authority = new Authority();
		orders = new HashSet<Order>();
		purchasedItems = new HashSet<PurchasedItem>();
		shoppingCartItemUnits = new HashSet<ShoppingCartItemUnit>();
		itemsToSell = new HashSet<AvailableItem>();
		wishedItems = new HashSet<AvailableItem>();

		userAccount = new UserAccount();
		authority.setAuthority("CUSTOMER");
		userAccount.addAuthority(authority);

		result = new Customer();
		result.setUserAccount(userAccount);
		result.setOrders(orders);
		result.setOrderedItems(purchasedItems);
		result.setAvailableItems(itemsToSell);
		result.setShoppingCartItemUnits(shoppingCartItemUnits);
		result.setWishList(wishedItems);

		return result;
	}

	@SuppressWarnings("deprecation")
	public Customer save(Customer customer) {
		Assert.notNull(customer);
		Date currentMoment;
		Customer res;
		currentMoment = new Date();

		if (customer.getId() == 0) {
			Assert.isTrue((customer.getCreditCard().getExpirationYear() - 1900) >= currentMoment
					.getYear());
			if ((customer.getCreditCard().getExpirationYear() - 1900) == currentMoment
					.getYear()) {
				Assert.isTrue((customer.getCreditCard().getExpirationMonth() - 1) > currentMoment
						.getMonth());
			}

			Assert.isNull(findByUsername(customer.getUserAccount()
					.getUsername()));

			Assert.isNull(administratorService.findByUsername(customer
					.getUserAccount().getUsername()));
		}

		res = customerRepository.save(customer);

		return res;
	}

	public Customer findOne(int userId) {
		Customer result;

		result = customerRepository.findOne(userId);

		return result;
	}

	public Collection<Customer> findAll() {
		Collection<Customer> result;
		Administrator principal;
		principal = administratorService.findByPrincipal();
		Assert.isTrue(principal instanceof Administrator);
		result = customerRepository.findAll();
		return result;
	}

	// Other Business Methods
	public Customer findByPrincipal() {
		Customer result;
		UserAccount userAccount;

		userAccount = LoginService.getPrincipal();
		Assert.notNull(userAccount);

		result = findByUserAccount(userAccount);
		Assert.notNull(result);

		return result;
	}

	public Customer findByUserAccount(UserAccount userAccount) {
		Assert.notNull(userAccount);
		Customer result;

		result = customerRepository.findByUserAccountId(userAccount.getId());

		return result;
	}

	public Customer findByUsername(String username) {
		Assert.notNull(username);
		Customer result;
		result = customerRepository.findByUsername(username);
		return result;
	}

	public Collection<Customer> findMoreSpentByBuyer() {

		Administrator principal;
		principal = administratorService.findByPrincipal();
		Assert.isTrue(principal instanceof Administrator);

		Collection<Object[]> objects;
		Customer buyer;
		List<Customer> res;
		Double totalPriceMax;
		List<Integer> idres;

		totalPriceMax = 0.0;
		res = new LinkedList<Customer>();
		idres = new LinkedList<Integer>();
		objects = orderRepository.findMoreSpentByBuyer();

		for (Object[] o : objects) {
			if (o[0] == null) {
				continue;
			} else {
				int idBuyer = (int) o[0];
				Double totalPrice = (Double) o[1];

				if (totalPriceMax < totalPrice) {
					totalPriceMax = totalPrice;
					idres.clear();
					idres.add(idBuyer);
				} else if (totalPriceMax == totalPrice) {
					idres.add(idBuyer);
				}
			}
		}
		for (Integer l : idres) {
			buyer = findOne(l);
			res.add(buyer);

		}

		return res;
	}

	public Collection<Customer> findMoreEarnedBySeller() {

		Administrator principal;
		principal = administratorService.findByPrincipal();
		Assert.isTrue(principal instanceof Administrator);

		Collection<Object[]> objects;
		Customer seller;
		List<Customer> res;
		Double priceMax;
		List<Integer> idres;

		priceMax = 0.0;
		res = new LinkedList<Customer>();
		idres = new LinkedList<Integer>();
		objects = purchasedItemRepository.findMoreEarnedBySeller();

		for (Object[] o : objects) {
			if (o[0] == null) {
				continue;
			} else {
				int idSeller = (int) o[0];
				Double price = (Double) o[1];

				if (priceMax < price) {
					priceMax = price;
					idres.clear();
					idres.add(idSeller);
				} else if (priceMax == price) {
					idres.add(idSeller);
				}
			}
		}
		for (Integer l : idres) {
			seller = findOne(l);
			res.add(seller);

		}

		return res;

	}

	// Reconstruction method
	public Customer reconstruct(CustomerForm customerForm) {
		Assert.notNull(customerForm);
		Customer result;
		Md5PasswordEncoder encoder = new Md5PasswordEncoder();
		String password = customerForm.getPassword();
		String hash = encoder.encodePassword(password, null);

		result = create();
		result.getUserAccount().setUsername(customerForm.getUsername());
		result.getUserAccount().setPassword(hash);
		result.setName(customerForm.getName());
		result.setSurname(customerForm.getSurname());
		result.setEmail(customerForm.getEmail());
		result.setCreditCard(customerForm.getCreditCard());
		result.setAddress(customerForm.getAddress());

		return result;
	}
}
